package com.telrob.antlr;

import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.List;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.misc.Interval;
import org.antlr.v4.runtime.tree.ParseTree;

import com.google.gson.Gson;
import com.telrob.antlr.role.XnBaseVisitor;
import com.telrob.antlr.role.XnParser.AssignmentContext;
import com.telrob.antlr.role.XnParser.BaseTypeContext;
import com.telrob.antlr.role.XnParser.CalculateBodyContext;
import com.telrob.antlr.role.XnParser.ConditionBlockContext;
import com.telrob.antlr.role.XnParser.ConditionContext;
import com.telrob.antlr.role.XnParser.ConditionIfBlockContext;
import com.telrob.antlr.role.XnParser.DeclareContext;
import com.telrob.antlr.role.XnParser.DoWhileBlockContext;
import com.telrob.antlr.role.XnParser.ElseBlockContext;
import com.telrob.antlr.role.XnParser.ElseIfBlockContext;
import com.telrob.antlr.role.XnParser.ExpressionContext;
import com.telrob.antlr.role.XnParser.ExpressionTypeContext;
import com.telrob.antlr.role.XnParser.ForBlockContext;
import com.telrob.antlr.role.XnParser.ForConditionBlockContext;
import com.telrob.antlr.role.XnParser.FunctionBlockContext;
import com.telrob.antlr.role.XnParser.FunctionBodyContext;
import com.telrob.antlr.role.XnParser.FunctionCallContext;
import com.telrob.antlr.role.XnParser.FunctionCallParamContext;
import com.telrob.antlr.role.XnParser.FunctionHanderContext;
import com.telrob.antlr.role.XnParser.FunctionParamContext;
import com.telrob.antlr.role.XnParser.FunctionParamDeclareContext;
import com.telrob.antlr.role.XnParser.IfBlockContext;
import com.telrob.antlr.role.XnParser.ProgContext;
import com.telrob.antlr.role.XnParser.StringContext;
import com.telrob.antlr.role.XnParser.WhileBlockContext;
import com.telrob.antlr.type.AssignmentType;
import com.telrob.antlr.type.ConditionBlockType;
import com.telrob.antlr.type.ConditionIfBlockType;
import com.telrob.antlr.type.ConditionType;
import com.telrob.antlr.type.DeclaraListType;
import com.telrob.antlr.type.DeclaraType;
import com.telrob.antlr.type.DigitType;
import com.telrob.antlr.type.DoWhileBlock;
import com.telrob.antlr.type.ElseBlockType;
import com.telrob.antlr.type.ElseIfBlockType;
import com.telrob.antlr.type.ExpressionType;
import com.telrob.antlr.type.ForBlockType;
import com.telrob.antlr.type.ForConditionBlockType;
import com.telrob.antlr.type.FunctionBodyType;
import com.telrob.antlr.type.FunctionCallParamType;
import com.telrob.antlr.type.FunctionCallType;
import com.telrob.antlr.type.FunctionHeaderType;
import com.telrob.antlr.type.FunctionParamType;
import com.telrob.antlr.type.FunctionType;
import com.telrob.antlr.type.IdType;
import com.telrob.antlr.type.IfBlockType;
import com.telrob.antlr.type.IntType;
import com.telrob.antlr.type.StringType;
import com.telrob.antlr.type.StringValue;
import com.telrob.antlr.type.WhileBlockType;
import com.telrob.antlr.type.XnType;

public class XnVisitor extends XnBaseVisitor<XnType> {
	private String outFile;
	private CharStream input=null;
	private String start=""
			+ "jmp code;5个字节\r\n" + 
			"stack:\r\n" + 
			"dw 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0\r\n" + 
			"code:\r\n" + 
			"MOVRN ax,stack\r\n" + 
			"MOVRR ss,ax\r\n" + 
			"MOVRN sp,0\r\n"+ 
			"call main\r\n"+ 
			"exit\r\n";
	
	public XnVisitor(){
		super();
	}
	public XnVisitor(CharStream input) {
		this.input=input;
	}
	@Override
	public XnType visitProg(ProgContext ctx) {
		//System.err.println(ctx.getText());
		int count=ctx.getChildCount();
		ProcessUtils processUtils=new ProcessUtils();
		Gson gson=new Gson();
		ScriptEngine eg=new ScriptEngineManager().getEngineByExtension("js");
		for(int i=0;i<count;i++) {
			XnType tp=visit(ctx.getChild(i));
			if(tp==null) {
				continue;
			}
			
			if(tp instanceof DeclaraListType) {
				processUtils.addDeclaraList((DeclaraListType)tp);
			}else if(tp instanceof FunctionType) {
				processUtils.addFunction((FunctionType)tp);
			}
			//System.err.println(tp.getClass());
			String json=gson.toJson(tp);
			try {
				Object obj=eg.eval("unescape('"+json+"')");
				//System.err.println(obj);
			}catch(Exception e) {
				
			}
		}
		
		processUtils.process();
		String code=processUtils.getAsmCode();
		
		String result=start+code;
		//System.err.println(start+code);
		try {
			//FileOutputStream fout=new FileOutputStream("code.xn");
			FileOutputStream fout=new FileOutputStream(outFile);
			fout.write(result.getBytes());
			fout.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return processUtils;
	}
	
	@Override
	public XnType visitString(StringContext ctx) {
		//获取原始数据
		int a = ctx.start.getStartIndex();
		int b = ctx.stop.getStopIndex();
		Interval inta=new Interval(a,b);
		String text=input.getText(inta);
		return new StringValue(text);
	}
	
	//函数调用
	public XnType visitFunctionCall(FunctionCallContext ctx) {
		String name=ctx.getChild(0).getText();
		XnType param=visit(ctx.getChild(2));
		FunctionCallType func=new FunctionCallType(new IdType(name), param);
		return func;
	}
	
	public XnType visitFunctionCallParam(FunctionCallParamContext ctx) {
		int count=ctx.getChildCount();
		FunctionCallParamType param=new FunctionCallParamType();
		for(int i=0;i<count;i+=2) {
			XnType item=visit(ctx.getChild(i));
			param.add(item);
		}
		return param;
	}
	
	//for循环
	public XnType visitForBlock(ForBlockContext ctx) {
		XnType condition=visit(ctx.getChild(2));
		XnType body=visit(ctx.getChild(5));
		XnType result=new ForBlockType(condition, body);
		return result;
	}
	//for循环条件
	public XnType visitForConditionBlock(ForConditionBlockContext ctx) {
		XnType p1=visit(ctx.getChild(0));
		XnType p2=visit(ctx.getChild(2));
		XnType p3=visit(ctx.getChild(4));
		XnType result=new ForConditionBlockType(p1, p2, p3);
		return result;
	}
	
	//dowhile
	public XnType visitDoWhileBlock(DoWhileBlockContext ctx) {
		XnType body=visit(ctx.getChild(2));
		XnType condition=visit(ctx.getChild(6));
		XnType result=new DoWhileBlock(condition, body);
		return result;
	}
	
	//while循环
	public XnType visitWhileBlock(WhileBlockContext ctx) {
		XnType condition=visit(ctx.getChild(2));
		XnType body=visit(ctx.getChild(5));
		XnType result=new WhileBlockType(condition, body);
		return result;
	}
	
	
	public XnType visitCalculateBody(CalculateBodyContext ctx) {
		return visit(ctx.getChild(0));
	}
	
	public XnType visitConditionIfBlock(ConditionIfBlockContext ctx) {
		int count=ctx.getChildCount();
		XnType ifCondition=null;
		XnType elseCondition=null;
		List<XnType> elseIfBlock=new ArrayList<XnType>();
		
		for(int i=0;i<count;i++) {
			XnType type=visit(ctx.getChild(i));
			if(type instanceof IfBlockType) {
				ifCondition=type;
			}else if(type instanceof ElseIfBlockType) {
				elseIfBlock.add(type);
			}else if(type instanceof ElseBlockType) {
				elseCondition=type;
			}
		}
		return new ConditionIfBlockType(ifCondition, elseIfBlock, elseCondition);
	}
	
	public XnType visitElseBlock(ElseBlockContext ctx) {
		XnType body=visit(ctx.getChild(2));
		XnType result=new ElseBlockType(body);
		return result;
	}
	
	public XnType visitElseIfBlock(ElseIfBlockContext ctx) {
		XnType condition= visit(ctx.getChild(3));
		XnType body= visit(ctx.getChild(6));
		XnType result= new ElseIfBlockType(condition, body);
		return result;
	}
	public XnType visitIfBlock(IfBlockContext ctx) {
		XnType condition=visit(ctx.getChild(2));
		XnType body=visit(ctx.getChild(5));
		XnType result=new IfBlockType(condition, body);
		return result;
	}
	
	
	//函数解析
	public XnType visitFunctionBlock(FunctionBlockContext ctx) {
		XnType header=visit(ctx.getChild(0));
		XnType body=visit(ctx.getChild(2));
		return new FunctionType(header, body);
	}
	
	//函数头解析
	public XnType visitFunctionHander(FunctionHanderContext ctx) {
		int count=ctx.getChildCount();
		XnType type=visit(ctx.getChild(0));
		String nameStr=ctx.getChild(1).getText();
		XnType name=new IdType(nameStr);
		XnType param=null;
		if(count==5) {
			param=visit(ctx.getChild(3));
		}
		return new FunctionHeaderType(type, name, param);
	}
	
	//函数参数解析
	public XnType visitFunctionParam(FunctionParamContext ctx) {
		int count=ctx.getChildCount();
		FunctionParamType param=new FunctionParamType();
		for(int i=0;i<count;i+=2) {
			XnType tp=visit(ctx.getChild(i));
			param.addChild(tp);
		}
		return param;
	}
	
	//解析函数参数
	public XnType visitFunctionParamDeclare(FunctionParamDeclareContext ctx) {
		XnType type=visit(ctx.getChild(0));
		String name=ctx.getChild(1).getText();
		return new DeclaraType(type, new IdType(name));
	}
	
	
	//函数体解析
	public XnType visitFunctionBody(FunctionBodyContext ctx) {
		int count=ctx.getChildCount();
		FunctionBodyType body=new FunctionBodyType();
		for(int i=0;i<count;i++) {
			ParseTree pt=ctx.getChild(i);
			XnType tp=visit(pt);
			body.addChild(tp);
		}
		return body;
	}
	
	
	public XnType visitConditionBlock(ConditionBlockContext ctx) {
		List<ConditionBlockContext> list=ctx.conditionBlock();
		if(list.size()>0) {
			if(list.size()==1) {
				return getCondition(list.get(0));
			}else if(list.size()==2) {
				String opt=ctx.getChild(1).getText();
				XnType val1=getCondition(list.get(0));
				XnType val2=getCondition(list.get(1));
				return new ConditionBlockType(val1, opt, val2);
			}
		}else {
			XnType d=visit(ctx.getChild(0));
			return d;
		}
		System.out.println(ctx.getText()+":"+list.size());
		return super.visitConditionBlock(ctx);
	}
	
	private XnType getCondition(ConditionBlockContext ctx) {
		List<ConditionBlockContext> list=ctx.conditionBlock();
		if(list.size()==0) {
			int count=ctx.getChildCount();
			//对有括号的情况进行处理
			XnType tp=null;
			if(count>1) {
				tp=visit(ctx.getChild(1));
			}else {
				tp=visit(ctx.getChild(0));
			}
			return tp;
		}else if(list.size()==1) {
			return getCondition(list.get(0));
		}else if(list.size()==2) {
			String opt=ctx.getChild(1).getText();
			XnType val1=getCondition(list.get(0));
			XnType val2=getCondition(list.get(1));
			return new ConditionBlockType(val1, opt, val2);
		}
		return null;
	}
	
	public XnType visitCondition(ConditionContext ctx) {
		String opt=ctx.getChild(1).getText();
		List<ExpressionContext> list=ctx.expression();
		XnType val1=visit(list.get(0));
		XnType val2=visit(list.get(1));
		return new ConditionType(val1,opt,val2);
	}
	
	public XnType visitDeclare(DeclareContext ctx) {
		int count=ctx.getChildCount();
		XnType type=visit(ctx.getChild(0));
		DeclaraListType list=new DeclaraListType();
		for(int i=1;i<count;i+=2) {
			ParseTree pt=ctx.getChild(i);
			XnType val=visit(pt);
			XnType result=null;
			if(val==null) {
				result=new DeclaraType(type, new IdType(pt.getText()));
			}else if(val instanceof AssignmentType) {
				AssignmentType v=(AssignmentType)(val);
				result=new DeclaraType(type, v);
			}else {
				System.err.println(val+":"+pt.getText());
			}
			list.add(result);
		}
		return list;
	}
	
	public XnType visitAssignment(AssignmentContext ctx) {
		String name=ctx.getChild(0).getText();
		IdType nameType=new IdType(name);
		ExpressionContext exp=ctx.expression();
		XnType value=parseExp(exp);
		XnType type=new AssignmentType(nameType, value);
		return type;
	}
	
	

	//简单的加减运算
	public XnType visitExpression(ExpressionContext ctx) {
		int count=ctx.getChildCount();
		if(count==1) {
			return visit(ctx.getChild(0)); 
		}else if(count==3) {
			String opt=ctx.getChild(1).getText();
			List<ExpressionContext> list=ctx.expression();
			if(list.size()==1) {
				return parseExp(list.get(0));
			}else if(list.size()==2){
				XnType val1=parseExp(list.get(0));
				XnType val2=parseExp(list.get(1));
				XnType result= new ExpressionType(val1, opt, val2);
				return result;
				
			}
		}
		return super.visitExpression(ctx);
	}
	
	//基础数据类型判断
	public XnType visitBaseType(BaseTypeContext ctx) {
		String text=ctx.getText();
		if("int".equals(text)) {
			return new IntType(text);
		}else if("string".equals(text)) {
			return new StringType(text);
		}
		return super.visitBaseType(ctx);
	}
	
	public XnType visitExpressionType(ExpressionTypeContext ctx) {
		if(ctx.ID()!=null) {
			return new IdType(ctx.getText());
		}else if(ctx.Digit()!=null) {
			return new DigitType(ctx.getText());
		}
		return super.visitExpressionType(ctx);
	}
	//遍历
	private XnType parseExp(ExpressionContext ctx){
		int count=ctx.getChildCount();
		String text=ctx.getText();
		if(count==1) {
			XnType t=visit(ctx.getChild(0));
			return t;
		}else if(count==3) {
			List<ExpressionContext> list=ctx.expression();
			String opt=ctx.getChild(1).getText();
			if(list.size()==1) {
				return parseExp(list.get(0));
			}else {
				XnType val1=parseExp(list.get(0));
				XnType val2=parseExp(list.get(1));
				//直接对已经能够确定的数据进行计算
				if((val1 instanceof DigitType)&&(val2 instanceof DigitType)) {
					String value="0";
					DigitType v1=(DigitType)val1;
					DigitType v2=(DigitType)val2;
					int vv1=Integer.parseInt(v1.getValue());
					int vv2=Integer.parseInt(v2.getValue());
					if("+".equals(opt)) {
						value=(vv1+vv2)+"";
					}else if("-".equals(opt)) {
						value=(vv1-vv2)+"";
					}else if("*".equals(opt)) {
						value=(vv1*vv2)+"";
					}else if("/".equals(opt)) {
						value=(vv1/vv2)+"";
					}else {
						value=(vv1%vv2)+"";
					}
					return new DigitType(value);
				}
				return new ExpressionType(val1, opt, val2);
			}
		}else {
			System.err.println("["+count+":"+text+"]");
		}
		return null;
	}

	public String getOutFile() {
		return outFile;
	}

	public void setOutFile(String outFile) {
		this.outFile = outFile;
	}
}
